package com.zhongpengcheng.blog.config;

import com.zhongpengcheng.blog.auth.filter.AuthFilter;
import com.zhongpengcheng.blog.auth.AuthRealm;
import com.zhongpengcheng.blog.properties.ShiroProperties;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.authc.credential.CredentialsMatcher;
import org.apache.shiro.mgt.DefaultSubjectDAO;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.mgt.DefaultWebSessionStorageEvaluator;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.boot.autoconfigure.web.ServerProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.servlet.Filter;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;

/**
 * Shiro配置
 * note: 1.记录变更 by zhongpengcheng
 *
 * @author zhongpengcheng
 * @date 2021/08/13 16:06
 **/
@Configuration
@Slf4j
@EnableConfigurationProperties(ShiroProperties.class)
public class ShiroConfiguration {
    /**
     * 认证和授权逻辑
     */
    @Bean
    public AuthRealm authRealm(CredentialsMatcher credentialsMatcher) {
        AuthRealm authRealm = new AuthRealm();
        authRealm.setCredentialsMatcher(credentialsMatcher);
        return authRealm;
    }

    /**
     * 安全管理器
     * @return -
     */
    @Bean
    public DefaultWebSecurityManager securityManager(AuthRealm authRealm) {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setRealm(authRealm);
        securityManager.setRememberMeManager(null);

        DefaultSubjectDAO subjectDAO = new DefaultSubjectDAO();
        DefaultWebSessionStorageEvaluator sessionStorageEvaluator = new DefaultWebSessionStorageEvaluator();
        sessionStorageEvaluator.setSessionStorageEnabled(false);
        subjectDAO.setSessionStorageEvaluator(sessionStorageEvaluator);
        securityManager.setSubjectDAO(subjectDAO);
        return securityManager;
    }

    /**
     * Filter工厂，用于设置过滤条件及跳转配置
     * @param securityManager 安全管理器
     * @return -
     */
    @Bean
    public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager, ServerProperties serverProperties,
                                              ShiroProperties properties) {
        ShiroFilterFactoryBean shiroFilter = new ShiroFilterFactoryBean();
        shiroFilter.setSecurityManager(securityManager);
        // filter注册到shiro，注意，不要将filter注册为bean
        Map<String, Filter> filterMap = new HashMap<>(2);
        filterMap.put("authFilter", new AuthFilter(serverProperties.getError()));
        shiroFilter.setFilters(filterMap);
        // 认证链构造参数
        Map<String, String> filterChainMap = new LinkedHashMap<>();
        // 登录接口放行
        properties.getOpen().forEach(anonURL -> filterChainMap.put(anonURL, "anon"));
        filterChainMap.put("/**", "authFilter");
        // 用户访问接口需要权限
        shiroFilter.setFilterChainDefinitionMap(filterChainMap);
        return shiroFilter;
    }

    /**
     * 生命周期处理器你
     */
    @Bean("lifecycleBeanPostProcessor")
    public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
        return new LifecycleBeanPostProcessor();
    }

    /**
     * 不知道干嘛的
     */
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
        advisor.setSecurityManager(securityManager);
        return advisor;
    }

    /**
     * 开启Shiro的注解(如@RequiresRoles,@RequiresPermissions)
     * 配置以下两个bean(DefaultAdvisorAutoProxyCreator和AuthorizationAttributeSourceAdvisor)即可实现此功能
     *
     * @return DefaultAdvisorAutoProxyCreator
     */
    @Bean
    public DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator() {
        DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
        advisorAutoProxyCreator.setProxyTargetClass(true);
        return advisorAutoProxyCreator;
    }
}
